/*
* DragViewGroup.java $version 2016. 04. 14.
*
* Copyright 2016 LINE Corporation. All rights Reserved. 
* LINE Corporation PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package view.dragon.com.draggridview;

import android.animation.LayoutTransition;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;

/**
 * @author chenjiulong
 */
public class DragViewGroup extends ViewGroup {
    private static final boolean DEBUG = true;
    private static final String TAG = "DragViewGroup";
    private int columnCount = 2;
    private float lastX = 0;
    private float lastY = 0;
    private Bitmap overlayBitmap = null;
    private Rect rect;
    private Paint paint;
    private Matrix matrix;
    private boolean interceptFlag = false;
    private View selectedView;
    private int distance;

    private int lastIndex = -1;

    public DragViewGroup(Context context) {
        this(context, null);
    }

    public DragViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
        initAttributes(context, attrs);
        init();
    }

    private void initAttributes(Context context, AttributeSet attributeSet) {
        TypedArray ta = context.obtainStyledAttributes(attributeSet, R.styleable.DragViewGroup, 0, 0);
        if (ta != null) {
            int indexCount = ta.getIndexCount();
            for (int i = 0; i < indexCount; i++) {
                int attrId = ta.getIndex(i);
                switch (attrId) {
                    case R.styleable.DragViewGroup_layout_columns:
                        columnCount = ta.getInt(attrId, 2);
                        break;
                }
            }
            ta.recycle();
        }
    }

    private void init() {
        ViewConfiguration vc = ViewConfiguration.get(getContext());
        distance = vc.getScaledTouchSlop() / 3;
        paint = new Paint();
        matrix = new Matrix();
        matrix.setScale(1.1f, 1.1f);

        LayoutTransition layoutTransition = getLayoutTransition();
        if (layoutTransition != null) {
            layoutTransition.setAnimator(LayoutTransition.APPEARING, null);
            layoutTransition.setAnimator(LayoutTransition.DISAPPEARING, null);
            layoutTransition.setAnimator(LayoutTransition.CHANGING, null);
            layoutTransition.setAnimator(LayoutTransition.CHANGE_APPEARING, null);
        }
    }

    public void interruptTouch(View view) {
        View childView = findParentView(view);
        if (childView != null) {
            childView.setDrawingCacheEnabled(true);
            Bitmap bitmap = childView.getDrawingCache();
            Rect rect = new Rect();
            childView.getLocalVisibleRect(rect);
            rect.offset(childView.getLeft(), childView.getTop());
            if (bitmap != null) {
                addOverlayBitmap(bitmap, rect, childView);
                childView.setVisibility(View.INVISIBLE);
            }
        }
    }

    private View findParentView(View view) {
        if (view.getParent() != this) {
            return findParentView((View) view.getParent());
        }
        return view;
    }

    private void addOverlayBitmap(Bitmap bitmap, Rect rect, View selectedView) {
        if (overlayBitmap != null) {
            overlayBitmap.recycle();
            overlayBitmap = null;
        }
        this.rect = rect;
        overlayBitmap = Bitmap.createBitmap(bitmap);
        postInvalidate();
        interceptFlag = true;
        this.selectedView = selectedView;
        this.lastIndex = indexOfChild(selectedView);
        matrix.reset();
        matrix.setScale(1.1f, 1.1f, overlayBitmap.getWidth() >> 1, overlayBitmap.getHeight() >> 1);
    }

    private void removeOverLayBitmap() {
        if (overlayBitmap != null) {
            overlayBitmap.recycle();
            overlayBitmap = null;
            rect = null;
            interceptFlag = false;
        }
        if (selectedView != null) {
            selectedView.setVisibility(View.VISIBLE);
        }
    }

    private void updatePosition() {
        if (rect != null) {
            int centerX = rect.centerX();
            int centerY = rect.centerY();
            int childCount = getChildCount();
            View childView;
            DragLayoutParams params;

            if (lastIndex >= 0) {
                childView = getChildAt(lastIndex);
                params = (DragLayoutParams) childView.getLayoutParams();
                if (centerX > params.left && centerX < params.right && centerY > params.top && centerY < params.bottom) {
                    if (DEBUG) {
                        Log.v(TAG, "updatePosition same view");
                    }
                    return;
                }
            }
            for (int i = 0; i < childCount; i++) {
                childView = getChildAt(i);
                params = (DragLayoutParams) childView.getLayoutParams();
                if (centerX > params.left && centerX < params.right && centerY > params.top && centerY < params.bottom) {
                    if (DEBUG) {
                        Log.v(TAG, String.format("updatePosition change view current:%d lastIndex:%d", i, lastIndex));
                    }
                    removeView(selectedView);
                    addView(selectedView, i);
                    lastIndex = i;
                    return;
                }
            }
        }
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        if (overlayBitmap != null) {
            if (!overlayBitmap.isRecycled()) {
                canvas.save();
                canvas.clipRect(rect);
                canvas.translate(rect.left, rect.top);
                canvas.drawBitmap(overlayBitmap, matrix, paint);
                canvas.restore();
            }
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
            lastX = ev.getX();
            lastY = ev.getY();
        }
        if (interceptFlag) {
            requestDisallowInterceptTouchEvent(true);
            return true;
        }
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getActionMasked();
        switch (action) {
            case MotionEvent.ACTION_MOVE:
                float currentX = event.getX();
                float currentY = event.getY();
                float dX = currentX - lastX;
                float dY = currentY - lastY;
                if (Math.abs(dX) > distance || Math.abs(dY) > distance) {
                    rect.offset((int) dX, (int) dY);
                    lastY = currentY;
                    lastX = currentX;
                    invalidate();
                    updatePosition();
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                removeOverLayBitmap();
                break;
        }
        return true;
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int columnWidth = width / columnCount;
        int maxHeight = 0;
        int measureCount = 0;
        int tempHeight;
        int maxLineHeight = 0;
        int childCount = getChildCount();
        View childView;

        for (int i = 0; i < childCount; i++) {
            childView = getChildAt(i);
            if (childView.getVisibility() != View.GONE) {
                measureChild(childView, MeasureSpec.makeMeasureSpec(columnWidth, MeasureSpec.getMode(widthMeasureSpec)), heightMeasureSpec);
                tempHeight = childView.getMeasuredHeight();
                if (tempHeight > maxLineHeight) {
                    maxLineHeight = tempHeight;
                }
                measureCount++;
                if (measureCount % columnCount == 0) {
                    maxHeight += maxLineHeight;
                    maxLineHeight = 0;
                }
            }
        }
        if (measureCount % columnCount != 0) {
            maxHeight += maxLineHeight;
        }
        setMeasuredDimension(widthMeasureSpec, resolveSize(maxHeight, heightMeasureSpec));
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (DEBUG) {
            Log.v(TAG, String.format("onLayout changed:%b l:%d t:%d r:%d b:%d", changed, l, t, r, b));
        }
        int visibleCount = 0;
        int childCount = getChildCount();
        int columnWidth = getMeasuredWidth() / columnCount;
        int lineStart = 0;
        int maxLineHeight = 0;
        int tempHeight;

        View childView;
        int modRet;
        for (int i = 0; i < childCount; i++) {
            childView = getChildAt(i);
            if (childView.getVisibility() != View.GONE) {
                visibleCount++;
                tempHeight = childView.getMeasuredHeight();

                if (tempHeight > maxLineHeight) {
                    maxLineHeight = tempHeight;
                }
                DragLayoutParams params = (DragLayoutParams) childView.getLayoutParams();

                modRet = visibleCount % columnCount;
                if (modRet == 0) {
                    params.left = columnWidth * (columnCount - 1);
                    params.top = lineStart;
                    params.right = params.left + childView.getMeasuredWidth();
                    params.bottom = lineStart + tempHeight;
                    lineStart += maxLineHeight;
                    maxLineHeight = 0;
                } else {
                    params.left = columnWidth * (modRet - 1);
                    params.top = lineStart;
                    params.right = params.left + childView.getMeasuredWidth();
                    params.bottom = lineStart + tempHeight;
                }
                childView.layout(params.left, params.top, params.right, params.bottom);
                if (DEBUG) {
                    Log.v(TAG, String.format("onLayout l:%d t:%d r:%d b:%d", params.left, params.top, params.right, params.bottom));
                }
            }
        }
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new DragLayoutParams(getContext(), attrs);
    }

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new DragLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    }

    @Override
    protected LayoutParams generateLayoutParams(LayoutParams p) {
        return new DragLayoutParams(p);
    }


    public static class DragLayoutParams extends ViewGroup.LayoutParams {
        int left;
        int top;
        int right;
        int bottom;

        public DragLayoutParams(LayoutParams source) {
            super(source);
        }

        public DragLayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
        }

        public DragLayoutParams(int width, int height) {
            super(width, height);
        }
    }
}
